/**
 * Created On: Aug 23, 2012
 */
package com.athena.dal.dao;

import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.EntityExistsException;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;

import org.springframework.transaction.annotation.Transactional;

/**
 * Default implementation of the Generic DAO.
 * 
 * @author Ashish Kumar Sinha <aksinha.mail@gmail.com>
 * Version 1.0
 */
public class GenericDaoImpl implements GenericDao {
	
	@PersistenceContext
	private EntityManager entityManager;
	
	public void setEntityManager(EntityManager entityManager) {
		this.entityManager = entityManager;
	}
	
	/* (non-Javadoc)
	 * @see com.athena.dal.dao.GenericDao#getEntityManager()
	 */
	@Override
	public EntityManager getEntityManager() {
		return entityManager;
	}

	/*
	 * (non-Javadoc)
	 * @see com.athena.dal.dao.GenericDao#findById(java.lang.Class, java.io.Serializable)
	 */
	@Override
	@Transactional(readOnly = true)
	public <T> T findById(Class<T>entityClass, Serializable ID) {
		return entityManager.find(entityClass, ID);
	}
	
	protected <T> TypedQuery<T> prepareTypedQuery(Class<T> entityClass, String queryName, Map<String, Object> parameters) {
		if(entityClass == null || queryName == null || queryName.isEmpty()) { return null; }
		
		if(entityManager != null){
			TypedQuery<T> query = entityManager.createNamedQuery(queryName, entityClass);
			if(query != null){
				if(parameters != null && !parameters.isEmpty()) {
					/*
					 * set parameters if provided.
					 */
					Set<String> paramSet = parameters.keySet();
					for (String param : paramSet) {
						query = query.setParameter(param, parameters.get(param));
					}
				}
				return query;
			}else {
				System.err.println(entityClass + " :: Created TypedQuery for query name is NULL. Query Name: " + queryName);
			}
		}else {
			System.err.println(entityClass + " :: " + queryName+ " :: Entity Manager is NULL");
		}
		return null;
	}
	
	/* (non-Javadoc)
	 * @see com.athena.dal.dao.GenericDao#findByNameQuery(java.lang.Class, java.lang.String, java.util.Map)
	 */
	@Override
	@Transactional(readOnly = true)
	public <T> T findByNameQuery(Class<T> entityClass, String queryName, Map<String, Object> parameters) {
		
		TypedQuery<T> query = prepareTypedQuery(entityClass, queryName, parameters);
		
		if(query != null){
			/*
			 * execute the query and get result.
			 */
			return query.getSingleResult();
		}
		return null;
	}

	/* (non-Javadoc)
	 * @see com.athena.dal.dao.GenericDao#findListByNameQuery(java.lang.Class, java.lang.String, java.util.Map)
	 */
	@Override
	public <T> List<T> findAllByNameQuery(Class<T> entityClass, String queryName, Map<String, Object> parameters) {
		TypedQuery<T> query = prepareTypedQuery(entityClass, queryName, parameters);
		
		if(query != null){
			/*
			 * execute the query and get result.
			 */
			return query.getResultList();
		}
		return null;
	}

	/* (non-Javadoc)
	 * @see com.athena.dal.dao.GenericDao#createOrUpdate(java.lang.Object)
	 */
	@Override
	@Transactional
	public <T> T createOrUpdate(T entity) {
		try{
			entityManager.persist(entity);
			entityManager.flush();
		}catch(EntityExistsException eee){
			entity = entityManager.merge(entity);
		}
		entityManager.flush();
		return entity;
	}

	/* (non-Javadoc)
	 * @see com.athena.dal.dao.GenericDao#create(java.lang.Object)
	 */
	@Override
	@Transactional
	public <T> T create(T entity) {
		entityManager.persist(entity);
		entityManager.flush();
		return entity;
	}

	/* (non-Javadoc)
	 * @see com.athena.dal.dao.GenericDao#update(java.lang.Object)
	 */
	@Override
	@Transactional
	public <T> T update(T entity) {
		entity = entityManager.merge(entity);
		entityManager.flush();
		return entity;
	}

	/* (non-Javadoc)
	 * @see com.athena.dal.dao.GenericDao#delete(java.lang.Object)
	 */
	@Override
	@Transactional
	public <T> void delete(T entity) {
		if(entity != null){
			entityManager.remove(entity);
			entityManager.flush();
		}
		
	}

}
